package ws.performancetest.tests.synchronization.lock;

import ws.performancetest.tests.synchronization.task.AbstractTask;
import ws.performancetest.tests.synchronization.task.TConsumer;
import ws.performancetest.tests.synchronization.task.TProducer;
import org.junit.Before;
import java.util.Arrays;
import java.util.Collection;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized;
import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Test class for producer-consumer using SemImpl.
 * 
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
@RunWith(Parameterized.class)
public class SemImplTestPC {

  /**
   * Test id. One id for each set of parameters.
   */
  private static int testId = 0;
  private int repP;
  private int repC;
  private int numberOfProducers;
  private int numberOfConsumers;
  /**
   * Expected size of the list after all operations.
   */
  private int expResult;
  /**
   * Shared list instance.
   */
  private SemImpl instance;
  
  private Thread[] threadList;

  /**
   * Create a new test instance.
   * 
   * @param size  Initial size for the shared list.
   * @param max   Maximum size for the shared list.
   * @param rep   Number of repetitions.
   * @param nP    Number of producers.
   * @param nC    Number of consumers.
   * @param expResult Expected size of the list after all operations.
   */
  public SemImplTestPC(int repP, int repC, int nP, int nC, int expResult) {
    testId++;

    this.repP = repP;
    this.repC = repC;
    this.numberOfProducers = nP;
    this.numberOfConsumers = nC;
    this.expResult = expResult;
  }

  /**
   * Create test parameters.
   *
   * @return list with parameters.
   */
  @Parameters
  public static Collection<Object[]> createParameters() {
    Object[][] data = new Object[][]{
      /* {initial list size, max list size, number of repetions, 
       * number of producers, number of consumers, expected result}
       */
      {1, 1, 1, 0, 1},
      {1, 1, 1, 1, 0},
      {1, 2, 2, 1, 0},
    };

    return Arrays.asList(data);
  }

  /**
   * Run this before every test.
   * 
   * @throws Exception
   */
  @Before
  public void setUp() throws Exception {
    //instance = new SemImpl(size, max);
    instance = new SemImpl();

    threadList = new Thread[numberOfProducers + numberOfConsumers];
    for (int i = 0; i < numberOfProducers; ++i) {
      AbstractTask task = new TProducer();
      task.initialize(instance, null, repP, testId);
      threadList[i] = new Thread(task);
      threadList[i].start();
    }
    for (int i = numberOfProducers; i < threadList.length; ++i) {
      AbstractTask task = new TConsumer();
      task.initialize(instance, null, repC, testId);
      threadList[i] = new Thread(task);
      threadList[i].start();
    }
  }

  /**
   * Test of add and remove methods, of class SemImpl.
   */
  @Test
  public void testPC() throws InterruptedException {
    System.out.println("add(), id=" + testId);

    for (Thread t : threadList) {
      t.join();
    }

    int result = instance.list;
    assertEquals(expResult, result);
  }
}
